package com.cmc.cloud.cmclink.doc.service.impl.si;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cmc.cloud.cmclink.bdt.api.base.dto.resp.VesselNameDataRespDTO;
import com.cmc.cloud.cmclink.doc.api.document.emums.CntrStatusEnum;
import com.cmc.cloud.cmclink.doc.api.document.emums.SiValidEnum;
import com.cmc.cloud.cmclink.doc.api.document.req.cargomanifest.TjBlnoCntrReportVO;
import com.cmc.cloud.cmclink.doc.api.document.req.cargomanifest.TjBlnoCntrReqVO;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.BlnoCntrErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.BlnoErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.convert.BlnoCntrConvert;
import com.cmc.cloud.cmclink.doc.entity.BlnoCntrDO;
import com.cmc.cloud.cmclink.doc.entity.BlnoDO;
import com.cmc.cloud.cmclink.doc.entity.VgmDO;
import com.cmc.cloud.cmclink.doc.mapper.BlnoCntrMapper;
import com.cmc.cloud.cmclink.doc.mapper.BlnoMapper;
import com.cmc.cloud.cmclink.doc.mapper.VgmMapper;
import com.cmc.cloud.cmclink.doc.rpcservice.VesselTransferService;
import com.cmc.cloud.cmclink.doc.service.BlnoCntrService;
import com.cmc.cloud.cmclink.doc.util.VslvoyUtil;
import com.cmc.cloud.cmclink.doc.vo.blnocntrvo.*;
import com.cmc.cloud.cmclink.doc.vo.blnolocalcostvo.*;
import com.cmc.cloud.cmclink.svl.api.base.resp.VesselVoyRespDTO;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.common.pojo.PageResult;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 提单货物信息 Service 实现类
 *
 * @author 集运管理员
 */
@Service
@Validated
@Slf4j
public class BlnoCntrServiceImpl implements BlnoCntrService {

    @Resource
    private BlnoCntrMapper blnoCntrMapper;

    @Resource
    private BlnoMapper blnoMapper;

    @Resource
    private VgmMapper vgmMapper;

    @Resource
    private VesselTransferService vesselTransferService;
    @Resource
    private VslvoyUtil vslvoyUtil;

    @Override
    public void createBlnoCntr(BlnoCntrCreateReqVO createReqVO) {
        // 插入
        BlnoCntrDO blnoCntr = BlnoCntrConvert.INSTANCE.convert(createReqVO);
        blnoCntrMapper.insert(blnoCntr);
    }

    @Override
    public void updateBlnoCntr(BlnoCntrUpdateReqVO updateReqVO) {
        // 校验存在
        validateBlnoCntrExists(updateReqVO.getId());
        // 更新
        BlnoCntrDO updateObj = BlnoCntrConvert.INSTANCE.convert(updateReqVO);
        blnoCntrMapper.updateById(updateObj);
    }

    @Override
    public void deleteBlnoCntr(Long id) {
        // 校验存在
        validateBlnoCntrExists(id);
        // 删除
        blnoCntrMapper.deleteById(id);
    }

    @Override
    public void deleteBlnoCntrByBlnoId(Long blnoId) {
        LambdaQueryWrapper<BlnoCntrDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BlnoCntrDO::getBlnoId, blnoId);
        blnoCntrMapper.delete(queryWrapper);
    }

    @Override
    public List<BlnoCntrDO> getBlnoCntrByBlnoId(Long blnoId) {
        LambdaQueryWrapper<BlnoCntrDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BlnoCntrDO::getBlnoId, blnoId);
        queryWrapper.orderByAsc(BlnoCntrDO::getId);
        return blnoCntrMapper.selectList(queryWrapper);
    }

    @Override
    public List<BlnoCntrUpdateReqVO> getBlnoCntrNewByBlnoId(Long blnoId, String bkgVesselCode, String bkgVoyage) {
        List<BlnoCntrDO> cntrDos = this.getBlnoCntrByBlnoId(blnoId);

        if (CollectionUtil.isNotEmpty(cntrDos)) {
            List<BlnoCntrUpdateReqVO> updateReqVos = BeanUtil.copyToList(cntrDos, BlnoCntrUpdateReqVO.class);
            List<VgmDO> vgmDos = vgmMapper.selectList(new LambdaQueryWrapperX<VgmDO>()
                    .eq(VgmDO::getVesselCode, bkgVesselCode)
                    .eq(VgmDO::getVoyage, bkgVoyage)
                    .inIfPresent(VgmDO::getCntrNo, cntrDos.stream().map(BlnoCntrDO::getCntrNo).collect(Collectors.toList())));
            if (CollectionUtil.isNotEmpty(vgmDos)) {
                vgmDos.stream().collect(Collectors.groupingBy(VgmDO::getCntrNo));
                Map<String, VgmDO> cntrNoMap = vgmDos.stream()
                        .collect(Collectors.groupingBy(
                                VgmDO::getCntrNo,
                                Collectors.reducing(null, (first, second) -> first == null ? second : first)
                        ));
                updateReqVos.forEach(n -> {
                    VgmDO vgmDO = cntrNoMap.get(n.getCntrNo());
                    if (vgmDO != null) {
                        n.setVgmAddress(vgmDO.getAddress());
                        n.setVgmSignature(vgmDO.getSignature());
                        n.setVgmTime(Date.from(vgmDO.getTime().atZone(ZoneId.systemDefault()).toInstant()));
                        n.setVgmTypeCode(vgmDO.getTypeCode());
                        n.setVgmWeight(vgmDO.getWeight());
                    }
                });
            }
            return updateReqVos;
        } else {
            return null;
        }
    }

    @Override
    public List<BlnoCntrDO> getBlnoCntrByBlnoIds(Collection<Long> ids) {
        LambdaQueryWrapperX<BlnoCntrDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(BlnoCntrDO::getBlnoId, ids);
        return blnoCntrMapper.selectList(queryWrapperX);
    }

    @Override
    public List<BlnoCntrDO> getBlnoCntrByCntrNos(Collection<String> cntrNos) {
        LambdaQueryWrapperX<BlnoCntrDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(BlnoCntrDO::getCntrNo, cntrNos);
        return blnoCntrMapper.selectList(queryWrapperX);
    }

    private void validateBlnoCntrExists(Long id) {
        if (blnoCntrMapper.selectById(id) == null) {
            throw ServiceExceptionUtil.exception(BlnoCntrErrorCodeConstants.BLNO_CNTR_NOT_EXISTS);
        }
    }

    @Override
    public BlnoCntrDO getBlnoCntr(Long id) {
        return blnoCntrMapper.selectById(id);
    }

    @Override
    public List<BlnoCntrDO> getBlnoCntrList(Collection<Long> ids) {
        return blnoCntrMapper.selectBatchIds(ids);
    }

    @Override
    public List<BlnoCntrDO> getCntrByBlNo(String blNo) {
        LambdaQueryWrapper<BlnoDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BlnoDO::getBlNo, blNo).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue());
        BlnoDO blnoDO = blnoMapper.selectOne(queryWrapper);
        if (blnoDO == null) {
            throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.BLNO_NOT_EXISTS);
        }
        LambdaQueryWrapper<BlnoCntrDO> scnQueryWrapper = new LambdaQueryWrapper<>();
        scnQueryWrapper.eq(BlnoCntrDO::getBlnoId, blnoDO.getId());
        scnQueryWrapper.orderByAsc(BlnoCntrDO::getId);
        return blnoCntrMapper.selectList(scnQueryWrapper);
    }

    @Override
    public PageResult<BlnoCntrDO> getBlnoCntrPage(BlnoCntrPageReqVO reqVO) {
        IPage<BlnoCntrDO> page = Page.of(reqVO.getPageNo(), reqVO.getPageSize());

        IPage<BlnoCntrDO> result = blnoCntrMapper.selectPageList(page, reqVO);
        return new PageResult<BlnoCntrDO>(result.getRecords(), result.getTotal());
    }

    @Override
    public void createOrUpdateAndDeleteBatch(Long blnoId, List<BlnoCntrUpdateReqVO> newList,
                                             List<BlnoCntrUpdateReqVO> oldList) {
        // 无效数据直接返回
        if (CollectionUtil.isEmpty(newList) && CollectionUtil.isEmpty(oldList)) {
            return;
        }
        // 默认值
        if (CollectionUtil.isEmpty(newList)) {
            newList = new ArrayList<>();
        }
        if (CollectionUtil.isEmpty(oldList)) {
            oldList = new ArrayList<>();
        }
        List<BlnoCntrUpdateReqVO> createList = new ArrayList<>();
        List<BlnoCntrUpdateReqVO> updateList = new ArrayList<>();
        // 历史数据
        Set<Long> oldIds = oldList.stream().map(e -> e.getId()).collect(Collectors.toSet());
        newList.forEach(e -> {
            if (e.getId() != null) {
                if (oldIds.contains(e.getId())) {
                    // 正常更新
                    updateList.add(e);
                    oldIds.remove(e.getId()); // 剩下的数据需要删除
                } else {
                    // 数据更新非法
                    throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_NI_UPDATE);
                }
            } else {
                e.setBlnoId(blnoId);
                createList.add(e);
            }
        });
        if (CollectionUtil.isNotEmpty(oldIds)) {
            // 没有匹配上的就是前端删除的
            blnoCntrMapper.deleteBatchIds(oldIds);
        }
        if (CollectionUtil.isNotEmpty(createList)) {
            List<BlnoCntrDO> cntrCreateList = BeanUtil.copyToList(createList, BlnoCntrDO.class);
            blnoCntrMapper.insertBatch(cntrCreateList);
        }
        if (CollectionUtil.isNotEmpty(updateList)) {
            List<BlnoCntrDO> cntrUpdateList = BeanUtil.copyToList(updateList, BlnoCntrDO.class);
            blnoCntrMapper.updateBatch(cntrUpdateList);
        }
    }

    @Override
    public BlnoCntrCheckLclRespVO checkLcl(BlnoCntrCheckLclReqVO checkLclReqVO) {
        // 1. 根据船名和航次查询所有相关的提单主信息
        LambdaQueryWrapper<BlnoDO> blnoQueryWrapper = new LambdaQueryWrapper<>();
        blnoQueryWrapper.eq(BlnoDO::getBkgVesselCode, checkLclReqVO.getVesselCode()).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue());
        blnoQueryWrapper.eq(BlnoDO::getBkgVoyage, checkLclReqVO.getVoyage());
        List<BlnoDO> blnoList = blnoMapper.selectList(blnoQueryWrapper);

        // 2. 排除当前提单号的记录
        List<Long> blnoIds = blnoList.stream().filter(blno -> !blno.getBlNo().equals(checkLclReqVO.getBlNo()))
                .map(BlnoDO::getId).collect(Collectors.toList());

        // 3. 查询这些提单主信息关联的箱号信息
        boolean isLcl = false;
        if (!blnoIds.isEmpty()) {
            LambdaQueryWrapper<BlnoCntrDO> cntrQueryWrapper = new LambdaQueryWrapper<>();
            cntrQueryWrapper.in(BlnoCntrDO::getBlnoId, blnoIds);
            cntrQueryWrapper.eq(BlnoCntrDO::getCntrNo, checkLclReqVO.getCntrNo());
            List<BlnoCntrDO> cntrList = blnoCntrMapper.selectList(cntrQueryWrapper);

            // 4. 检查是否有匹配的箱号
            isLcl = !cntrList.isEmpty();
        }

        // 5. 返回结果
        BlnoCntrCheckLclRespVO response = new BlnoCntrCheckLclRespVO();
        response.setCntrNo(checkLclReqVO.getCntrNo());
        response.setLcl(isLcl);
        return response;
    }

    @Override
    public PageResult<BlnoCntrReportVO> getBlnoCntrReport(BlnoCntrReportReqVO reqVo) {
        IPage<BlnoCntrReportVO> page = Page.of(reqVo.getPageNo(), reqVo.getPageSize());
        IPage<BlnoCntrReportVO> blnoCntrReport = blnoCntrMapper.getBlnoCntrReport(page, reqVo);
        List<BlnoCntrReportVO> records = blnoCntrReport.getRecords();
        return new PageResult<>(records, blnoCntrReport.getTotal());
    }

    @Override
    public TjBlnoCntrReportVO getTjBlnoCntr(TjBlnoCntrReqVO reqVo) {
        TjBlnoCntrReportVO resVo = new TjBlnoCntrReportVO();
        List<TjBlnoCntrStatistic> blCntr = blnoCntrMapper.getTjBlnoCntr(reqVo, reqVo.getBlNos(), reqVo.getCntrTypes());
        if (CollectionUtil.isEmpty(blCntr)) {
            return null;
        }
        Map<String, VesselNameDataRespDTO> respDtoMap = new HashMap<>();
        try {
            respDtoMap = vesselTransferService.getVesselDataByCodes(Collections.singletonList(reqVo.getVesselCode()));
            log.info("getTjBlnoCntr_getVesselDataByCodes_info:{}", JSON.toJSONString(respDtoMap));
        } catch (Exception e) {
            log.error("getTjBlnoCntr_getVesselDataByCodes_error", e);
        }
        VesselNameDataRespDTO vesselNameData = respDtoMap.get(reqVo.getVesselCode());
        if (null != vesselNameData) {
            resVo.setVesselNameCn(vesselNameData.getVesselNameCn());
            resVo.setVesselNameEn(vesselNameData.getVesselNameEn());
            resVo.setImoCode(vesselNameData.getImoCode());
        }
        resVo.setVoyageIm(reqVo.getVoyageIm());
        VesselVoyRespDTO voyPtaCtm = vslvoyUtil.getVoyPtaCtm(reqVo.getPodCode(), reqVo.getVoyageIm(), reqVo.getVesselCode());
        if (null != voyPtaCtm) {
            resVo.setPodEta(voyPtaCtm.getPta());
            resVo.setCtmCode(voyPtaCtm.getCtmCode());
        }
        int twentyEmptyNum = blCntr.stream()
                .filter(o -> CntrStatusEnum.E.getValue().equals(o.getCntrStatus()))
                .mapToInt(TjBlnoCntrStatistic::getTwentySum).sum();
        int fortyEmptyNum = blCntr.stream()
                .filter(o -> CntrStatusEnum.E.getValue().equals(o.getCntrStatus()))
                .mapToInt(TjBlnoCntrStatistic::getFortySum).sum();
        int twentyFullNum = blCntr.stream()
                .filter(o -> CntrStatusEnum.F.getValue().equals(o.getCntrStatus()))
                .mapToInt(TjBlnoCntrStatistic::getTwentySum).sum();
        int fortyFullNum = blCntr.stream()
                .filter(o -> CntrStatusEnum.F.getValue().equals(o.getCntrStatus()))
                .mapToInt(TjBlnoCntrStatistic::getFortySum).sum();
        BigDecimal vesselDwt = blCntr.stream().map(TjBlnoCntrStatistic::getWgtSum).reduce(BigDecimal.ZERO, BigDecimal::add);
        int ncEmptyNum = twentyEmptyNum + fortyEmptyNum;
        int ncFullNum = twentyFullNum + fortyFullNum;
        int teuEmptyNum = twentyEmptyNum + fortyEmptyNum * 2;
        int teuFullNum = twentyFullNum + fortyFullNum * 2;
        resVo.setBlNum(reqVo.getBlNos().size());
        resVo.setTwentyEmptyNum(twentyEmptyNum);
        resVo.setTwentyFullNum(twentyFullNum);
        resVo.setFortyEmptyNum(fortyEmptyNum);
        resVo.setFortyFullNum(fortyFullNum);
        resVo.setNcEmptyNum(ncEmptyNum);
        resVo.setNcFullNum(ncFullNum);
        resVo.setTeuEmptyNum(teuEmptyNum);
        resVo.setTeuFullNum(teuFullNum);
        resVo.setVesselDwt(vesselDwt);
        resVo.setTotalPage(reqVo.getTotalPage());
        resVo.setManifestDate(reqVo.getManifestDate());
        resVo.setManifestOperator("朗涛");
        resVo.setPhoneNo("65720120");
        return resVo;

    }
}
